#include "stdio.h"
#include "../inc/adc.h"

ADC_HandleTypeDef g_adc_handle;
DMA_HandleTypeDef g_dma_adc_handle;   
uint16_t g_adc_dma_buf[ADC_SUM];                            /* 存储ADC原始值 */
uint16_t g_adc_val[ADC_CH_NUM];                             /* 存储ADC转换后的电压值 */

uint32_t adc_half_num = 0;
uint32_t adc_full_num = 0;

static void adc_channel_set(ADC_HandleTypeDef *adc_handle, uint32_t ch, uint32_t rank, uint32_t stime);

void adc_gpio_init(void)
{
    GPIO_InitTypeDef GPIO_InitStruct = {0};
    ADC_MOTOR_VOL_GPIO_CLK_ENABLE();
    ADC_TEMP_GPIO_CLK_ENABLE();
    ADC_U_CUR_GPIO_CLK_ENABLE();
    ADC_V_CUR_GPIO_CLK_ENABLE();
    ADC_W_CUR_GPIO_CLK_ENABLE();

    /*Configure GPIO pin : PA3 */
    GPIO_InitStruct.Pin = ADC_MOTOR_VOL_PIN;
    GPIO_InitStruct.Mode = GPIO_MODE_ANALOG;
    GPIO_InitStruct.Pull = GPIO_NOPULL;
    HAL_GPIO_Init(ADC_MOTOR_VOL_PORT, &GPIO_InitStruct);    

    GPIO_InitStruct.Pin = ADC_TEMP_PIN;
    HAL_GPIO_Init(ADC_TEMP_PORT, &GPIO_InitStruct);

    GPIO_InitStruct.Pin = ADC_U_CUR_PIN;
    HAL_GPIO_Init(ADC_U_CUR_PORT, &GPIO_InitStruct);

    GPIO_InitStruct.Pin = ADC_V_CUR_PIN;
    HAL_GPIO_Init(ADC_V_CUR_PORT, &GPIO_InitStruct);

    GPIO_InitStruct.Pin = ADC_W_CUR_PIN;
    HAL_GPIO_Init(ADC_W_CUR_PORT, &GPIO_InitStruct);
}


static void adc_init(void)
{
    g_adc_handle.Instance = ADC1;                                             /*指定ADC句柄控制的是ADC1外设*/
    g_adc_handle.Init.ClockPrescaler = ADC_CLOCK_SYNC_PCLK_DIV2;               /*设置ADC时钟为同步PCLK时钟,2分频*/
    g_adc_handle.Init.Resolution = ADC_RESOLUTION_12B;                        /*设置ADC分辨率为12位*/
    g_adc_handle.Init.DataAlign = ADC_DATAALIGN_RIGHT;                        /*设置数据对齐方式为右对齐*/
    g_adc_handle.Init.GainCompensation = 0;                                   /*禁用增益补偿*/
    g_adc_handle.Init.ScanConvMode = ADC_SCAN_ENABLE;                         /*使能扫描模式, 用于多通道自动转换*/
    g_adc_handle.Init.EOCSelection = ADC_EOC_SEQ_CONV;                        /*设置在整个序列转换完成后才产生EOC标志*/
    g_adc_handle.Init.LowPowerAutoWait = DISABLE;                             /*禁用低功耗自动等待模式*/
    g_adc_handle.Init.ContinuousConvMode = ENABLE;                            /*使能连续转换模式, 转换结束后自动开始新的转换*/
    g_adc_handle.Init.NbrOfConversion = 5;                                    /*设置常规转换序列中的转换通道数量为5*/
    g_adc_handle.Init.DiscontinuousConvMode = DISABLE;                        /*禁用不连续/间断转换模式*/
    g_adc_handle.Init.ExternalTrigConv = ADC_SOFTWARE_START;                  /*设置触发源为软件触发*/
    g_adc_handle.Init.ExternalTrigConvEdge = ADC_EXTERNALTRIGCONVEDGE_NONE;   /*由于是软件触发, 所以不需要触发边沿*/
    g_adc_handle.Init.DMAContinuousRequests = ENABLE;                         /*使能连续DMA请求, 以配合连续转换模式*/
    g_adc_handle.Init.Overrun = ADC_OVR_DATA_OVERWRITTEN;                     /*设置数据溢出时, 新数据覆盖旧数据*/
    g_adc_handle.Init.OversamplingMode = DISABLE;                             /*禁用过采样模式*/

    if (HAL_ADC_Init(&g_adc_handle) != HAL_OK)                                 
    {
        Error_Handler();
    }

    HAL_ADCEx_Calibration_Start(&g_adc_handle, ADC_SINGLE_ENDED);             /*启动ADC自校准程序(针对单端模式), 以提高精度*/

}



/**
 * @brief       ADC DMA读取 初始化函数
 *   @note      本函数还是使用adc_init对ADC进行大部分配置,有差异的地方再单独配置
 * @param       par         : 外设地址
 * @param       mar         : 存储器地址
 * @retval      无
 */
void adc_dma_init(void)
{
    __HAL_RCC_DMAMUX1_CLK_ENABLE();
    __HAL_RCC_DMA1_CLK_ENABLE();

    /* DMA配置 */
    g_dma_adc_handle.Instance = DMA1_Channel1;                             /* 设置DMA通道 */
    g_dma_adc_handle.Init.Request= DMA_REQUEST_ADC1;                  /* 设置DMA请求 */
    g_dma_adc_handle.Init.Direction = DMA_PERIPH_TO_MEMORY;                 /* DIR = 1 ,  外设到存储器模式 */
    g_dma_adc_handle.Init.PeriphInc = DMA_PINC_DISABLE;                     /* 外设非增量模式 */
    g_dma_adc_handle.Init.MemInc = DMA_MINC_ENABLE;                         /* 存储器增量模式 */
    g_dma_adc_handle.Init.PeriphDataAlignment = DMA_PDATAALIGN_HALFWORD;    /* 外设数据长度:16位 */
    g_dma_adc_handle.Init.MemDataAlignment = DMA_MDATAALIGN_HALFWORD;       /* 存储器数据长度:16位 */
    g_dma_adc_handle.Init.Mode = DMA_CIRCULAR;                              /* 外设流控模式 */
    g_dma_adc_handle.Init.Priority = DMA_PRIORITY_MEDIUM;                   /* 中等优先级 */
    HAL_DMA_Init(&g_dma_adc_handle);                                        /* 初始化DMA */
    
    // adc_init();                                                             /* 初始化ADC */

    /* 配置对应ADC通道 */
    adc_channel_set(&g_adc_handle, ADC_MOTOR_VOL_CHX, ADC_REGULAR_RANK_1, ADC_SAMPLETIME_640CYCLES_5);
    adc_channel_set(&g_adc_handle, ADC_TEMP_CHX, ADC_REGULAR_RANK_2, ADC_SAMPLETIME_640CYCLES_5);
    adc_channel_set(&g_adc_handle, ADC_U_CUR_CHX, ADC_REGULAR_RANK_3, ADC_SAMPLETIME_640CYCLES_5);
    adc_channel_set(&g_adc_handle, ADC_V_CUR_CHX, ADC_REGULAR_RANK_4, ADC_SAMPLETIME_640CYCLES_5);
    adc_channel_set(&g_adc_handle, ADC_W_CUR_CHX, ADC_REGULAR_RANK_5, ADC_SAMPLETIME_640CYCLES_5);
    
    __HAL_LINKDMA(&g_adc_handle, DMA_Handle,g_dma_adc_handle);
    
    HAL_NVIC_SetPriority(DMA1_Channel1_IRQn, 5 , 0);                        /* */
    HAL_NVIC_EnableIRQ(DMA1_Channel1_IRQn);                                /* 使能DMA中断 */
    
    HAL_ADC_Start_DMA(&g_adc_handle, (uint32_t*)&g_adc_dma_buf, ADC_SUM);   /* 开始DMA数据传输 */
}


void adc_module_init(void)
{
    adc_gpio_init();
    adc_init();
    adc_dma_init();
}


/**
 * @brief       设置ADC通道采样时间
 * @param       adcx : adc句柄指针,ADC_HandleTypeDef
 * @param       ch   : 通道号, ADC_CHANNEL_0~ADC_CHANNEL_17
 * @param       stime: 采样时间  0~7, 对应关系为:
 *   @arg       ADC_SAMPLETIME_2CYCLES_5,  2.5个ADC时钟周期        ADC_SAMPLETIME_6CYCLES_5, 6.5个ADC时钟周期
 *   @arg       ADC_SAMPLETIME_12CYCLES_5, 12.5个ADC时钟周期       ADC_SAMPLETIME_24CYCLES_5, 24.5个ADC时钟周期
 *   @arg       ADC_SAMPLETIME_47CYCLES_5, 47.5个ADC时钟周期       ADC_SAMPLETIME_92CYCLES_5, 92.5个ADC时钟周期
 *   @arg       ADC_SAMPLETIME_247CYCLES_5 , 247.5个ADC时钟周期    ADC_SAMPLETIME_640CYCLES_5,640.5个ADC时钟周期
 * @param       rank: 多通道采集时需要设置的采集编号,
                假设你定义channel1的rank=1，channel2的rank=2，
                那么对应你在DMA缓存空间的变量数组AdcDMA[0] 就是通道1的转换结果，AdcDMA[1]就是通道2的转换结果。 
                单通道设置为 ADC_REGULAR_RANK_1
 *   @arg       编号1~16：ADC_REGULAR_RANK_1~ADC_REGULAR_RANK_16
 * @retval      无
 */
static void adc_channel_set(ADC_HandleTypeDef *adc_handle, uint32_t ch, uint32_t rank, uint32_t stime)
{
    /* 配置对应ADC通道 */
    ADC_ChannelConfTypeDef adc_channel;
    adc_channel.Channel = ch;                   /* 设置ADCX对通道ch */
    adc_channel.Rank = rank;                    /* 设置采样序列 */
    adc_channel.SamplingTime = stime;           /* 设置采样时间 */
    adc_channel.SingleDiff = ADC_SINGLE_ENDED;  /* 单端输入模式 */
    adc_channel.OffsetNumber = ADC_OFFSET_NONE;
    adc_channel.Offset = 0;
    HAL_ADC_ConfigChannel( adc_handle, &adc_channel );   
}


/**
 * @brief       ADC转换完成的回调函数
 * @param       无
 * @retval      无
 */
// void HAL_ADC_ConvCpltCallback(ADC_HandleTypeDef* hadc)
// {
//     if (hadc->Instance == ADC1) 
//     { 
//         HAL_ADC_Stop_DMA(&g_adc_handle);                                            /* 关闭DMA转换 */
//         calc_adc_val(g_adc_dma_buf, g_adc_val, ADC_CH_NUM, ADC_DMA_BUF_SIZE);       /* ADC数值转换 */
//         HAL_ADC_Start_DMA(&g_adc_handle, (uint32_t *)&g_adc_dma_buf, (uint32_t)(ADC_CH_NUM * ADC_DMA_BUF_SIZE)); /* 再启动DMA转换 */
//     }
// }



/**
 * @brief       计算ADC的平均值（滤波）
 * @param       * p ：代入ADC原始值
 * @param       * buf ：存放滤波后的ADC值
 * @param       ch_num ：采集的ADC通道数量
 * @param       len ：每个通道的采集次数
 * @note        此函数对电压、温度、电流对应的ADC值进行滤波
 * @retval      无
 */
void calc_adc_val(uint16_t * p, uint16_t * buf , uint16_t ch_num, uint16_t len)
{
    uint32_t temp[ADC_CH_NUM] = {0};                        /* 定义一个缓存数组 */
    int i , j;                                              
    for (i = 0 ; i < len ; i++)                             /* 根据ADC通道数循环获取，并累加 */
    {
        for (j = 0 ; j < ch_num ; j++)                      /* 将采集到的ADC值，各通道进行累加 */
        {
            temp[j] += p[j + i * ch_num];
        }
    }
    for (j = 0 ; j < ch_num ; j++)
    {
        temp[j] /= len;                                     /* 获取平均值 */
        buf[j] = temp[j];                                   /* 将滤波后的值存到buf里 */
    }
}

void stop_adc(void)
{
    HAL_ADC_Stop_DMA(&g_adc_handle);
}

void start_adc(void)
{
    HAL_ADC_Start_DMA(&g_adc_handle, (uint32_t *)&g_adc_dma_buf, (uint32_t)(ADC_SUM));
}



void HAL_ADC_ConvHalfCpltCallback(ADC_HandleTypeDef* hadc)
{
    if (hadc->Instance == ADC1) 
    {
        adc_half_num++;
        calc_adc_val(g_adc_dma_buf, g_adc_val, ADC_CH_NUM, ADC_DMA_BUF_SIZE_HALF);
    }
}

void HAL_ADC_ConvCpltCallback(ADC_HandleTypeDef* hadc)
{
    if (hadc->Instance == ADC1) 
    {
        adc_full_num++;
        calc_adc_val(&g_adc_dma_buf[ADC_DMA_BUF_SIZE_HALF], g_adc_val, ADC_CH_NUM, ADC_DMA_BUF_SIZE_HALF);
    }
}


void DMA1_Channel1_IRQHandler(void)
{
    HAL_DMA_IRQHandler(&g_dma_adc_handle);
}


void show_adc_val(void)
{
    // printf("adc_half_num: %d\r\n", adc_half_num);
    // printf("adc_full_num: %d\r\n", adc_full_num);
    printf("adc_u:%d,",g_adc_val[2]); 
    printf("adc_v:%d,",g_adc_val[3]);
    printf("adc_w:%d\r\n",g_adc_val[4]);
    printf("adc_all:%d,\r\n",g_adc_val[2]+g_adc_val[3]+g_adc_val[4]);
}